package info.niwota.pasta;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class NoteProvider extends ContentProvider {

	private static final String TAG = "NoteProvider";

	/**
	 * This inner DatabaseHelper class defines methods to create and upgrade the
	 * database from previous versions.
	 */
	private static class DatabaseHelper extends SQLiteOpenHelper {
		public DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onCreate: " + db);
			}
			db.execSQL("CREATE TABLE " + NOTE_TABLE
					+ " (_id INTEGER PRIMARY KEY,"
					+ "title TEXT COLLATE LOCALIZED NOT NULL," + "body TEXT,"
					+ "times_accessed INTEGER,"
					+ "star INTEGER NOT NULL DEFAULT 0," + "attachment TEXT,"
					+ "data1 TEXT," + "data2 TEXT," + "data3 TEXT," + "created INTEGER,"
					+ "accessed INTEGER," + "modified INTEGER" + ");");
			//
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			if (Debug.DEBUG) {
				Log.d(TAG, "onUpgrade from version " + oldVersion
						+ " to " + newVersion);
			}
			// db.execSQL("DROP TABLE IF EXISTS notes");
			// onCreate(db);
		}
	}

	private static final String DATABASE_NAME = "notes.db";

	private static final int DATABASE_VERSION = 1;

	private static final int NOTE_NAME = 2;

	private static final int NOTE_SEARCH = 3;

	private static final int NOTES = 1;

	private static HashMap<String, String> NOTES_LIST_PROJECTION_MAP;

	private static final UriMatcher URI_MATCHER;

	private static final String NOTE_TABLE = "notes";

	/*
	 * This static block sets up the pattern matches for the various URIs that
	 * this content provider can handle. It also sets up the default projection
	 * block (which defines what columns are returned from the database for the
	 * results of a query).
	 */
	static {
		URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
		URI_MATCHER.addURI(Note.NOTE_AUTHORITY, "notes", NOTES);
		URI_MATCHER.addURI(Note.NOTE_AUTHORITY, "notes/*", NOTE_NAME);
		URI_MATCHER.addURI(Note.NOTE_AUTHORITY, "note/search/*", NOTE_SEARCH);

		NOTES_LIST_PROJECTION_MAP = new HashMap<String, String>();
		NOTES_LIST_PROJECTION_MAP.put(Note.Notes._ID, "_id");
		NOTES_LIST_PROJECTION_MAP.put(Note.Notes.TITLE, "title");
		NOTES_LIST_PROJECTION_MAP.put(Note.Notes.BODY, "body");
		NOTES_LIST_PROJECTION_MAP.put(Note.Notes.ATTACHMENT, "attachment");
		NOTES_LIST_PROJECTION_MAP.put(Note.Notes.CREATED_DATE, "created");
		NOTES_LIST_PROJECTION_MAP.put(Note.Notes.MODIFIED_DATE, "modified");
	}

	private DatabaseHelper dbHelper;

	/**
	 * Delete rows matching the where clause and args for the supplied URL. The
	 * URL can be either the all notes URL (in which case all notes matching the
	 * where clause will be deleted), or the note name, in which case it will
	 * delete the note with the exactly matching title. Any of the other URLs
	 * will be rejected as invalid. For deleting notes by title we use ReST
	 * style arguments from the URI and don't support where clause and args.
	 */
	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		/*
		 * This is kind of dangerous: it deletes all records with a single
		 * Intent. It might make sense to make this ContentProvider non-public,
		 * since this is kind of over-powerful and the provider isn't generally
		 * intended to be public. But for now it's still public.
		 */
		// if (Note.Notes.ALL_NOTES_URI.equals(uri)) {
		// return dbHelper.getWritableDatabase()
		// .delete(NOTE_TABLE, null, null);
		// }

		int count;
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		switch (URI_MATCHER.match(uri)) {
		case NOTES:
			count = db.delete(NOTE_TABLE, where, whereArgs);
			break;

		case NOTE_NAME:
			if (!TextUtils.isEmpty(where) || (whereArgs != null)) {
				throw new UnsupportedOperationException(
						"Cannot use where clause");
			}
			String noteId = uri.getPathSegments().get(1);
			count = db.delete(NOTE_TABLE, "_id=?", new String[] { noteId });
			break;

		default:
			throw new IllegalArgumentException("Unknown URL " + uri);
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	@Override
	public String getType(Uri uri) {
		switch (URI_MATCHER.match(uri)) {
		case NOTES:
		case NOTE_SEARCH:
			// for a notes list, or search results, return a mimetype
			// indicating
			// a directory of notes
			return "vnd.android.cursor.dir/vnd.pasta.note";

		case NOTE_NAME:
			// for a specific note name, return a mimetype indicating a single
			// item representing a cannednote
			return "vnd.android.cursor.item/vnd.pasta.note";

		default:
			// any other kind of URL is illegal
			throw new IllegalArgumentException("Unknown URL " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		long rowID;
		ContentValues values;
		if (initialValues != null) {
			values = new ContentValues(initialValues);
		} else {
			values = new ContentValues();
		}

		// We can only insert a note, no other URLs make sense here
		if (URI_MATCHER.match(uri) != NOTE_NAME) {
			throw new IllegalArgumentException("Unknown URL " + uri);
		}

		// Update the modified time of the record
		Long now = Long.valueOf(System.currentTimeMillis());

		// Make sure that the fields are all set
		if (values.containsKey(Note.Notes.CREATED_DATE) == false) {
			values.put(Note.Notes.CREATED_DATE, now);
		}

		if (values.containsKey(Note.Notes.MODIFIED_DATE) == false) {
			values.put(Note.Notes.MODIFIED_DATE, now);
		}

		if (values.containsKey(Note.Notes.TITLE) == false) {
			values.put(Note.Notes.TITLE, uri.getLastPathSegment());
		}

		if (values.containsKey(Note.Notes.BODY) == false) {
			values.put(Note.Notes.BODY, "");
		}

		if (values.containsKey(Note.Notes.ATTACHMENT) == false) {
			values.put(Note.Notes.ATTACHMENT, "");
		}

		SQLiteDatabase db = dbHelper.getWritableDatabase();
		rowID = db.insert(NOTE_TABLE, "body", values);
		if (rowID > 0) {
			// Uri newUri = Uri.withAppendedPath(Note.Notes.ALL_NOTES_URI, uri
			// .getLastPathSegment());
			Uri newUri = Uri.withAppendedPath(Note.Notes.ALL_NOTES_URI, Long
					.toString(rowID));
			getContext().getContentResolver().notifyChange(newUri, null);
			return newUri;
		}

		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public boolean onCreate() {
		// On the creation of the content provider, open the database,
		// the Database helper will create a new version of the database
		// if needed through the functionality in SQLiteOpenHelper
		dbHelper = new DatabaseHelper(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sort) {
		// Query the database using the arguments provided
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		String[] whereArgs = null;

		// What type of query are we going to use - the URL_MATCHER
		// defined at the bottom of this class is used to pattern-match
		// the URL and select the right query from the switch statement
		switch (URI_MATCHER.match(uri)) {
		case NOTES:
			// this match lists all notes
			qb.setTables(NOTE_TABLE);
			qb.setProjectionMap(NOTES_LIST_PROJECTION_MAP);
			break;

		case NOTE_SEARCH:
			// this match searches for a text match in the body of notes
			qb.setTables(NOTE_TABLE);
			qb.setProjectionMap(NOTES_LIST_PROJECTION_MAP);
			qb.appendWhere("body like ? or title like ?");
			whereArgs = new String[2];
			whereArgs[0] = whereArgs[1] = "%" + uri.getLastPathSegment() + "%";
			break;

		case NOTE_NAME:
			// this match searches for an exact match for a specific note name
			qb.setTables(NOTE_TABLE);
			qb.appendWhere("title=?");
			whereArgs = new String[] { uri.getLastPathSegment() };
			break;

		default:
			// anything else is considered and illegal request
			throw new IllegalArgumentException("Unknown URL " + uri);
		}

		// If no sort order is specified use the default
		String orderBy;
		if (TextUtils.isEmpty(sort)) {
			orderBy = Note.Notes.DEFAULT_SORT_ORDER;
		} else {
			orderBy = sort;
		}

		// Run the query and return the results as a Cursor
		SQLiteDatabase mDb = dbHelper.getReadableDatabase();
		Cursor c = qb.query(mDb, projection, null, whereArgs, null, null,
				orderBy);
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

	/**
	 * The update method, which will allow either a mass update using a where
	 * clause, or an update targeted to a specific note name (the latter will be
	 * more common). Other matched URLs will be rejected as invalid. For
	 * updating notes by title we use ReST style arguments from the URI and do
	 * not support using the where clause or args.
	 */
	@Override
	public int update(Uri uri, ContentValues values, String where,
			String[] whereArgs) {
		int count;
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		switch (URI_MATCHER.match(uri)) {
		case NOTES:
			count = db.update(NOTE_TABLE, values, where, whereArgs);
			break;

		case NOTE_NAME:
			values.put(Note.Notes.MODIFIED_DATE, System.currentTimeMillis());
			count = db.update(NOTE_TABLE, values, where, whereArgs);
			break;

		default:
			throw new IllegalArgumentException("Unknown URL " + uri);
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

}
